/**
AirwayPathfinder for X-Plane Flight Simulator. Calculates the shortest path between two NavAids. 
Copyright (C) 2011  Nils Buletta

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 **/

package de.buletta.airwaypathfinder;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListSelectionModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.jdesktop.swingx.JXMapKit;
import org.jdesktop.swingx.JXMapKit.DefaultProviders;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.mapviewer.AbstractTileFactory;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.jdesktop.swingx.mapviewer.Waypoint;

/**
 * The GUI for the Pathfinder program.
 * 
 * @author Nils Buletta
 * @version 0.1
 */
public class Gui extends JFrame {
	private static final long serialVersionUID = 2L;
	protected JXMapKit mapKit = new JXMapKit();
	protected DataHolder holder;
	protected MapMouseListener mapMouseListener = new MapMouseListener(this);
	protected MapMouseWheelListener mapMouseWheelListener = new MapMouseWheelListener(
			this);
	protected NavsRenderer renderer = new NavsRenderer();
	protected PathWaypointPainter<JXMapViewer> painter;
	protected EarthNav nav1;
	protected EarthNav nav2;
	protected boolean toggle = false;
	protected JTextField nav1Text;
	protected JTextField nav2Text;
	protected JPanel southPanel;
	protected JPanel southPanel2;
	protected ButtonGroup layerGroup;
	protected JButton computePathButton;
	protected JTextField earthNavTextField;
	protected JTextField fixTextField;
	protected JTextField awyTextField;
	protected JTextField airportTextField;
	protected JTextField fmsStoreTextField;
	protected JPanel prefPanel;
	protected JButton earthNavPathButton;
	protected JButton fixPathButton;
	protected JButton awyPathButton;
	protected JButton airportPathButton;
	protected JButton fmsStorePathButton;
	protected JFileChooser fileChooser;
	protected Gui thisGui = this;
	protected JTable table = new JTable();

	public Gui() {
		super("AirwayPathfinder");
	}

	private void createAndShowGui() {
		try {
			// set the look and feel to system look and feel
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (UnsupportedLookAndFeelException e) {
			// handle exception
		} catch (ClassNotFoundException e) {
			// handle exception
		} catch (InstantiationException e) {
			// handle exception
		} catch (IllegalAccessException e) {
			// handle exception
		}

		// putting together the table that shows the computed path
		table.setFillsViewportHeight(true);
		table.getSelectionModel().addListSelectionListener(new RowListener());
		table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);

		// create a data holder and initialize it in another thread
		holder = new DataHolder();
		DataPrepareWorker dpw = new DataPrepareWorker();
		dpw.execute();

		// // load icons for the frame
		// ArrayList<Image> icons = new ArrayList<>();
		// icons.add(new ImageIcon(this.getClass().getResource(
		// "/images/icon16.png")).getImage());
		// icons.add(new ImageIcon(this.getClass().getResource(
		// "/images/icon22.png")).getImage());
		// icons.add(new ImageIcon(this.getClass().getResource(
		// "/images/icon24.png")).getImage());
		// icons.add(new ImageIcon(this.getClass().getResource(
		// "/images/icon64.png")).getImage());
		// icons.add(new ImageIcon(this.getClass().getResource(
		// "/images/icon128.png")).getImage());
		//
		// // set icons for the frame
		// setIconImages(icons);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setLocation(40, 40);
		// this.setSize(800, 600);

		// create the menu bar
		MenuBar menuBar = new MenuBar();
		Menu fileMenu = new Menu("File");

		MenuItem preferencesMenuItem = new MenuItem("Preferences");
		// preferencesMenuItem.setActionCommand("preferences");
		preferencesMenuItem
				.addActionListener(new PreferencesMenuItemListener());
		fileMenu.add(preferencesMenuItem);

		menuBar.add(fileMenu);

		setMenuBar(menuBar);

		// increase number of threads used to load tiles for the map
		((AbstractTileFactory) mapKit.getMainMap().getTileFactory())
				.setThreadPoolSize(10);
		mapKit.setDefaultProvider(DefaultProviders.OpenStreetMaps);
		// mapKit.setCenterPosition(new GeoPosition(0,0));
		mapKit.getMainMap().addMouseListener(mapMouseListener);
		mapKit.getMainMap().addMouseWheelListener(mapMouseWheelListener);

		// create a painter who paints the waypoints and connects them
		painter = new PathWaypointPainter<>();

		// set a new renderer to controll how nav points are drawn
		painter.setRenderer(renderer);
		painter.setWaypoints(holder.getNavsSet());
		mapKit.getMainMap().setOverlayPainter(painter);

		// has to be set to ensure the getViewportBounds() returns sensible
		// values
		mapKit.getMainMap().setHorizontalWrapped(false);

		// create the north panel
		JPanel northPanel = new JPanel();
		// JCheckBox airportToAirportCheckBox = new
		// JCheckBox("Airport to airport");
		// airportToAirportCheckBox.setSelected(false);
		// airportToAirportCheckBox.addItemListener(new
		// AirportToAirportListener(
		// this));
		JCheckBox showAirports = new JCheckBox("Show Airports");
		showAirports.setSelected(true);
		showAirports.addItemListener(new ShowAirportsListener(this));
		JCheckBox showNoAirwayNav = new JCheckBox("Show Navs without Airway");
		showNoAirwayNav.setSelected(false);
		showNoAirwayNav.addItemListener(new ShowNoAirwayNavItemListener(this));
		renderer.setShowNoAirwayNav(false);
		renderer.setShowAirports(true);

		// northPanel.add(airportToAirportCheckBox);
		northPanel.add(showAirports);
		northPanel.add(showNoAirwayNav);

		// create the south panel
		southPanel = new JPanel();
		JLabel nav1Label = new JLabel("NAV 1");
		JLabel nav2Label = new JLabel("NAV 2");
		nav1Text = new JTextField("", 5);
		nav2Text = new JTextField("", 5);
		nav1Text.setEditable(false);
		nav2Text.setEditable(false);

		computePathButton = new JButton("Compute Path");
		computePathButton.setActionCommand("ComputePath");
		computePathButton.addActionListener(new ComputePathButtonListener());

		JRadioButton lowerLayer = new JRadioButton("Lower Layer");
		JRadioButton upperLayer = new JRadioButton("Upper Layer");
		JRadioButton bothLayers = new JRadioButton("Both Layers");

		lowerLayer.setSelected(true);
		lowerLayer.setActionCommand("1");
		upperLayer.setActionCommand("2");
		bothLayers.setActionCommand("3");

		layerGroup = new ButtonGroup();

		layerGroup.add(lowerLayer);
		layerGroup.add(upperLayer);
		layerGroup.add(bothLayers);

		JPanel layerPanel = new JPanel();
		layerPanel.add(lowerLayer);
		layerPanel.add(upperLayer);
		layerPanel.add(bothLayers);

		southPanel.add(nav1Label);
		southPanel.add(nav1Text);
		southPanel.add(nav2Label);
		southPanel.add(nav2Text);
		southPanel.add(layerPanel);
		southPanel.add(computePathButton);

		getContentPane().setLayout(new BorderLayout());

		getContentPane().add(mapKit, BorderLayout.CENTER);

		// wait till the data holder is initialized
		while (!dpw.isDone()) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		if (!holder.isPrefsOK() && !holder.isDataOK()) {
			showPreferences();
		}
		mapKit.setPreferredSize(new Dimension(800, 600));
		getContentPane().add(southPanel, BorderLayout.SOUTH);
		getContentPane().add(northPanel, BorderLayout.NORTH);

		// add a MouseListener to the glass pane that consumes all mouse events
		// to prevent user input during path computation
		getGlassPane().addMouseListener(new MouseListener() {

			@Override
			public void mouseClicked(MouseEvent arg0) {
				arg0.consume();
			}

			@Override
			public void mouseEntered(MouseEvent arg0) {
				arg0.consume();
			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				arg0.consume();
			}

			@Override
			public void mousePressed(MouseEvent arg0) {
				arg0.consume();
			}

			@Override
			public void mouseReleased(MouseEvent arg0) {
				arg0.consume();
			}
		});

		pack();
		setVisible(true);
	}

	public void airportToAirport(boolean b) {
		mapMouseListener.selectAirports(b);
		// TODO change southpanel make navs not clickable
	}

	public void selectWaypoint(EarthNav w) {
		if (!toggle) {
			nav1 = w;
			toggle = true;
			nav1Text.setText(w.getIdentifier());
			return;
		}

		nav2 = w;
		toggle = false;
		nav2Text.setText(w.getIdentifier());
		return;
	}

	public void setNoAirwayNavsShown(boolean b) {
		renderer.setShowNoAirwayNav(b);
		repaintMap();
	}

	public void setShowAirports(boolean b) {
		renderer.setShowAirports(b);
		repaintMap();
	}

	public void repaintMap() {
		repaint();
	}

	// public void setDataHolder(DataHolder h) {
	// this.holder = h;
	// }

	/**
	 * Start the GUI
	 */
	public void start() {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGui();
			}
		});
	}

	public void showPreferences() {
		prefPanel = new JPanel();
		prefPanel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		// add label and textfield to specify the path to the earth_nav.dat file
		JLabel earthNavLabel = new JLabel("Path to earth_nav.dat");
		c.gridx = 0;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavLabel, c);
		earthNavTextField = new JTextField(holder.getPrefs().get(
				"EARTHNAVPATH", "Please specify"), 20);
		c.gridx = 1;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavTextField, c);
		earthNavPathButton = new JButton("Choose file");
		earthNavPathButton.addActionListener(new EarthNavPathButtonListener());
		c.gridx = 2;
		c.gridy = 0;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(earthNavPathButton, c);

		// add label and textfield to specify the path to the earth_fix.dat file
		JLabel fixLabel = new JLabel("Path to earth_fix.dat");
		c.gridx = 0;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixLabel, c);
		fixTextField = new JTextField(holder.getPrefs().get("FIXPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixTextField, c);
		fixPathButton = new JButton("Choose file");
		fixPathButton.addActionListener(new FixPathButtonListener());
		c.gridx = 2;
		c.gridy = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fixPathButton, c);

		// add label and textfield to specify the path to the earth_awy.dat file
		JLabel awyLabel = new JLabel("Path to earth_awy.dat");
		c.gridx = 0;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyLabel, c);
		awyTextField = new JTextField(holder.getPrefs().get("AWYPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyTextField, c);
		awyPathButton = new JButton("Choose file");
		awyPathButton.addActionListener(new AwyPathButtonListener());
		c.gridx = 2;
		c.gridy = 2;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(awyPathButton, c);

		// add label and textfield to specify the path to the apt.dat file
		JLabel airportLabel = new JLabel("Path to apt.dat");
		c.gridx = 0;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportLabel, c);
		airportTextField = new JTextField(holder.getPrefs().get("AIRPORTPATH",
				"Please specify"), 20);
		c.gridx = 1;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportTextField, c);
		airportPathButton = new JButton("Choose file");
		airportPathButton.addActionListener(new AirportPathButtonListener());
		c.gridx = 2;
		c.gridy = 3;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(airportPathButton, c);

		// add label and textfield to specify the path where the FMS plans
		// should be stored
		JLabel fmsStoreLabel = new JLabel("Folder to save FMS plans to ");
		c.gridx = 0;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStoreLabel, c);
		fmsStoreTextField = new JTextField(holder.getPrefs().get(
				"FMSSTOREPATH", "Please specify"), 20);
		c.gridx = 1;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStoreTextField, c);
		fmsStorePathButton = new JButton("Choose directory");
		fmsStorePathButton.addActionListener(new FMSStorePathButtonListener());
		c.gridx = 2;
		c.gridy = 4;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		prefPanel.add(fmsStorePathButton, c);

		// add save button
		JButton saveButton = new JButton("Save");
		c.gridx = 0;
		c.gridy = 5;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		saveButton.addActionListener(new SavePreferencesButtonListener());
		prefPanel.add(saveButton, c);

		// add abort button
		JButton abortButton = new JButton("Abort");
		c.gridx = 1;
		c.gridy = 5;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		abortButton.addActionListener(new AbortButtonListener());
		prefPanel.add(abortButton, c);

		// remove the map and show the preferences panel
		getContentPane().remove(mapKit);
		getContentPane().add(prefPanel, BorderLayout.CENTER);
		revalidate();
		repaint();
	}

	/**
	 * Listens for clicks on the preferences menu item.
	 * <p>
	 * Shows the preferences panel if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class PreferencesMenuItemListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			showPreferences();
		}
	}

	/**
	 * Listens for clickes on the save button located in the preferences panel.
	 * <p>
	 * Saves the preferences if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class SavePreferencesButtonListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			// create a new thread to save preferences and initialize the data
			SavePreferencesWorker spw = new SavePreferencesWorker();
			getGlassPane().setVisible(true);
			setCursor(new Cursor(Cursor.WAIT_CURSOR));
			spw.execute();
		}
	}

	/**
	 * Listens for clicks on the abort button located in the preferences panel.
	 * <p>
	 * Returns to the map view if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class AbortButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			getContentPane().remove(prefPanel);
			getContentPane().add(mapKit);
			revalidate();
			repaint();
		}
	}

	/**
	 * Listens for clicks on the back button located in the south panel if a
	 * path was calculated.
	 * 
	 * @author gambler
	 * 
	 */
	private class BackButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			getContentPane().remove(southPanel2);
			getContentPane().add(southPanel, BorderLayout.SOUTH);
			painter.setWaypoints(holder.getNavsSet());
			revalidate();
			repaint();
		}
	}

	/**
	 * Listens for clicks on the save button located in the south panel.
	 * <p>
	 * Saves the FMS to the specified fms store path.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class SaveFmsButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent evt) {

			// use PrtintWriter to format the output according to the os
			try (PrintWriter out = new PrintWriter(new File(holder.getPrefs()
					.get("FMSSTOREPATH", "default")
					+ System.getProperty("file.separator")
					+ nav1.getIdentifier()
					+ " - "
					+ nav2.getIdentifier()
					+ ".fms"))) {

				out.format("%s%n%s%n%s%n", "I", "3 version", "1");
				out.format("%d%n", painter.getWaypoints().size() - 1);
				for (Waypoint w : painter.getWaypoints()) {
					EarthNav wp = (EarthNav) w;
					out.format(Locale.US, "%d %s 0.000000 %f %f %n", wp
							.getType(), wp.getIdentifier(), wp.getPosition()
							.getLatitude(), wp.getPosition().getLongitude());

				}
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * Listens for clicks on the FMS store path button located in the
	 * preferences panel.
	 * <p>
	 * Shows a file chooser if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class FMSStorePathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			fileChooser = new JFileChooser();
			fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			if (fileChooser.showOpenDialog(thisGui) == JFileChooser.APPROVE_OPTION) {
				fmsStoreTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		}
	}

	/**
	 * Listens for clicks on the airport path button located in the preferences
	 * menu.
	 * <p>
	 * Shows a file chooser if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class AirportPathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(thisGui) == JFileChooser.APPROVE_OPTION) {
				airportTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		}
	}

	/**
	 * Listens for clicks on the airway path button.
	 * <p>
	 * Shows a file chooser if clicked
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class AwyPathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(thisGui) == JFileChooser.APPROVE_OPTION) {
				awyTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		}
	}

	/**
	 * Listens for clicks on fix path button located in the preferences panel.
	 * <p>
	 * Shows a file chooser if clicked.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class FixPathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(thisGui) == JFileChooser.APPROVE_OPTION) {
				fixTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		}
	}

	/**
	 * Listens for clicks on the computePathButton located in the south panel.
	 * <p>
	 * Starts a new {@link CalculatePathWorker} to calculate the path between
	 * the selected nav points.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class ComputePathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			if (nav1 == null || nav2 == null) {
				return;
			}

			// build a SwingWorker for the pathfinding to keep the gui
			// responsive
			SwingWorker<ArrayList<EarthNav>, Void> worker = new CalculatePathWorker();

			// set cursor to wainting cursor and activate glass pane
			thisGui.setCursor(new Cursor(Cursor.WAIT_CURSOR));
			getGlassPane().setVisible(true);
			revalidate();
			worker.execute();
		}
	}

	/**
	 * Listens for clicks on the button where you can specify the path to the
	 * earth_nav.dat file. This button is located in the preferences panel.
	 * <p>
	 * Opens a file chooser dialog to select the file.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class EarthNavPathButtonListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent evt) {
			fileChooser = new JFileChooser();
			if (fileChooser.showOpenDialog(thisGui) == JFileChooser.APPROVE_OPTION) {
				earthNavTextField.setText(fileChooser.getSelectedFile()
						.getAbsolutePath());
			}
		}

	}

	/**
	 * Listens for selections in the table that shows the computed path.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class RowListener implements ListSelectionListener {
		public void valueChanged(ListSelectionEvent e) {
			if (e.getValueIsAdjusting()) {
				return;
			}

			// set the center position of the map to the selected EarthNav
			mapKit.setAddressLocation(new GeoPosition((double) table.getModel()
					.getValueAt(table.getSelectedRow(), 1), (double) table
					.getModel().getValueAt(table.getSelectedRow(), 2)));
		}
	}

	/**
	 * Used to calculate the path between to navs in a separate thread.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class CalculatePathWorker extends
			SwingWorker<ArrayList<EarthNav>, Void> {
		@Override
		protected ArrayList<EarthNav> doInBackground() {

			return Pathfinder.calculatePath(nav1, nav2, Integer
					.parseInt(layerGroup.getSelection().getActionCommand()),
					holder.getNavs());
		}

		@Override
		protected void done() {
			try {
				ArrayList<EarthNav> list = get();
				if (list.size() > 0) {
					// table = new JTable();
					table.setModel(new PathTableModel(list));

					DecimalFormat df = new DecimalFormat("0.00 NM");
					painter.setWaypoints(new LinkedHashSet<Waypoint>(list));

					// get the GeoPositions from the waypoints
					Set<GeoPosition> zoomSet = new HashSet<>();
					for (EarthNav n : list) {
						zoomSet.add(n.getPosition());
					}

					// set a new zoom level so all waypoints are shown on the
					// map
					mapKit.getMainMap().calculateZoomFrom(zoomSet);

					southPanel2 = new JPanel();
					// JTextArea text = new JTextArea(10, 30);
					// text.setEditable(false);
					//
					// StringBuilder b = new StringBuilder();
					//
					// for (EarthNav m : list) {
					// b.append(m.getType() + " " + m.getIdentifier()
					// + " 00000.000000 "
					// + m.getPosition().getLatitude() + " "
					// + m.getPosition().getLongitude() + "\n");
					// }

					JLabel distanceLabel = new JLabel("Distance "
							+ df.format(+list.get(list.size() - 1)
									.getMoveCost() * 0.539956803));
					// JTextField distanceText = new
					// JTextField(df.format(+list.get(
					// list.size() - 1).getMoveCost() * 0.539956803));
					// distanceText.setEditable(false);
					// System.out.println(list.get(list.size()-1).getMoveCost()
					// *
					// 0.539956803);
					// text.setText(b.toString());
					// text.setEditable(false);
					// JScrollPane scroller = new JScrollPane(text);
					JScrollPane scroller = new JScrollPane(table);
					// table.setFillsViewportHeight(true);
					// table.getSelectionModel().addListSelectionListener(new
					// RowListener());
					// table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
					scroller.setPreferredSize(new Dimension(400, 120));

					JButton back = new JButton("Back");
					back.addActionListener(new BackButtonListener());

					JButton saveFMS = new JButton("Save FMS");
					saveFMS.addActionListener(new SaveFmsButtonListener());

					southPanel2.add(distanceLabel);
					// southPanel2.add(distanceText);
					southPanel2.add(scroller);
					southPanel2.add(back);
					southPanel2.add(saveFMS);

					getContentPane().remove(southPanel);
					getContentPane().add(southPanel2, BorderLayout.SOUTH);
					revalidate();
				}
			} catch (InterruptedException | ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {

				// reset cursor and hide glass pane
				thisGui.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
				getGlassPane().setVisible(false);
			}
		}
	}

	/**
	 * Used to initialize the data in the {@code DataHolder} object.
	 * 
	 * @author Nils Buletta
	 * 
	 */
	private class DataPrepareWorker extends SwingWorker<DataHolder, Void> {

		@Override
		protected DataHolder doInBackground() throws Exception {
			holder.initPreferences();
			holder.prepareData();
			return holder;
		}

		@Override
		protected void done() {
		}
	}

	/**
	 * Used to save the preferences and initialise the {@code DataHolder} object
	 * if it's not initialized.
	 * 
	 * @author gambler
	 * 
	 */
	private class SavePreferencesWorker extends SwingWorker<Boolean, Void> {

		@Override
		protected Boolean doInBackground() throws Exception {
			boolean temp = holder.savePreferences(thisGui);
			if (temp) {
				if (!holder.isDataOK()) {
					holder.prepareData();
				}
			}
			return Boolean.valueOf(temp);
		}

		@Override
		protected void done() {
			try {
				if (get().booleanValue()) {
					getContentPane().remove(prefPanel);
					getContentPane().add(mapKit);
				} else {
					JLabel errorLabel = new JLabel("Error saving Preferences");
					GridBagConstraints c = new GridBagConstraints();
					c.gridx = 0;
					c.gridy = 6;
					prefPanel.add(errorLabel, c);
				}
			} catch (InterruptedException | ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			getGlassPane().setVisible(false);
			setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			revalidate();
			repaint();
		}
	}
}